package PrjHttpUtils

import (
	"dgo/goutils/utils"
	"gitee.com/ymofen/supervalue"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"sync/atomic"
	"time"
)

var (
	Config_DefaultAllowCrossAccess   bool = false
	Config_DefaultEscapeResponseJSON bool = true
)

type func_after_response func(respHeader http.Header, body []byte)

type HttpRec struct {
	RespFlag int32 //   0: 未响应, 1:响应完成

	RespW http.ResponseWriter
	ReqR  *http.Request
	// 调用url.Query时每次都会parse
	urlQuery         url.Values
	Req              *wrapper.SuperValue
	Resp             *wrapper.SuperValue
	BodyBuf          []byte
	EncodeJSONEscape bool
	TagData          interface{}
	OnAction         func(action int) bool
	BeforeResponse   func() bool
	AfterResponse    func()

	afterResponseFunc []func_after_response

	/**
	  在响应之后执行
	*/
	OnResponseFunc func(respHeader http.Header, body []byte)

	/***
	  在响应之前执行
	*/
	OnBeforeResponseFunc func(sender *HttpRec) bool
	BeforeRespLs         *utils.Dispatch

	OnActionEx func(action int, args ...interface{}) (bool, interface{})
	OnFindEx   func(action int, args ...interface{}) (bool, interface{})
}

func NewHttpRec(w http.ResponseWriter, r *http.Request) *HttpRec {
	rval := &HttpRec{
		ReqR:  r,
		RespW: w,
	}
	rval.Initial()
	return rval
}

func (this *HttpRec) Initial() {
	if this.BeforeRespLs == nil {
		this.BeforeRespLs = utils.NewDispatch("")
	}

	if this.Resp == nil {
		this.Resp = wrapper.NewSVObject()
	}
	this.EncodeJSONEscape = Config_DefaultEscapeResponseJSON
	if Config_DefaultAllowCrossAccess {
		AllowCrossAccessSet(this.RespW)
	}
}

func (this *HttpRec) URLQuery() url.Values {
	if this.urlQuery == nil {
		this.urlQuery = this.ReqR.URL.Query()
	}
	return this.urlQuery
}

func (this *HttpRec) AddAfterResponseListener(fn func_after_response) {
	this.afterResponseFunc = append(this.afterResponseFunc, fn)
}

func (this *HttpRec) innerDoAfterResponseFunc(respHeader http.Header, body []byte) {

	if this.AfterResponse != nil {
		this.AfterResponse()
	}

	if this.OnResponseFunc != nil {
		this.OnResponseFunc(this.RespW.Header(), body)
	}

	for i := 0; i < len(this.afterResponseFunc); i++ {
		this.afterResponseFunc[i](respHeader, body)
	}
}

func (this *HttpRec) DecodeRequestBodyAsJSON() error {
	return this.DecodeRequestBodyAsJSONCache(false)
}

func (this *HttpRec) CheckReadBody() error {
	if len(this.BodyBuf) > 0 {
		return nil
	}
	s, err := ioutil.ReadAll(this.ReqR.Body)
	if err != nil {
		return err
	}
	this.BodyBuf = s
	return nil
}

func (this *HttpRec) DecodeRequestBodyAsJSONCache(cache bool) error {
	s, err := ioutil.ReadAll(this.ReqR.Body)
	if err != nil {
		return err
	}
	if len(s) == 0 {
		if this.Req == nil {
			this.Req = wrapper.NewSVObjectCache(cache)
		}
		return nil
	}
	this.BodyBuf = s
	this.Req = wrapper.NewSVObjectFromBufIfErrThenNewCache(s, cache)
	return nil
}

func (this *HttpRec) GetContentLength() int64 {
	return this.ReqR.ContentLength
}

func (this *HttpRec) CheckDecodeBody() error {
	content_type := strings.ToLower(this.ReqR.Header.Get("Content-Type"))
	if strings.Contains(content_type, "json") {
		this.DecodeRequestBodyAsJSONCache(false)
	}
	if strings.Contains(content_type, "text/plain") {
		this.DecodeRequestBodyAsJSONCache(false)
	}
	if this.Req == nil {
		this.Req = wrapper.NewSVObjectCache(false)
	}
	return nil
}

func (this *HttpRec) FindEx(action int, args ...interface{}) (bool, interface{}) {
	if this.OnFindEx == nil {
		return false, nil
	} else {
		return this.OnFindEx(action, args...)
	}
}

func (this *HttpRec) ActionEx(action int, args ...interface{}) (bool, interface{}) {
	if this.OnActionEx == nil {
		return false, nil
	} else {
		return this.OnActionEx(action, args...)
	}
}

func (this *HttpRec) QryAsInt64(key string, def int64) int64 {
	str := this.ReqR.URL.Query().Get(key)
	if len(str) == 0 {
		if this.Req != nil {
			return this.Req.Int64ByPath(key, def)
		} else {
			return def
		}
	}
	return utils.StrToInt64Def(str, def)
}

func (this *HttpRec) IntByName(key string, def int) int {
	return this.QryAsInt(key, def)
}

func (this *HttpRec) QryAsInt(key string, def int) int {
	str := this.ReqR.URL.Query().Get(key)
	if len(str) == 0 {
		if this.Req != nil {
			return this.Req.IntByPath(key, def)
		} else {
			return def
		}
	}
	return utils.StrToIntDef(str, def)
}

func (this *HttpRec) RangeURLParam(fn func(k, v string) bool) {
	for k, v := range this.ReqR.URL.Query() {
		if len(v) > 0 {
			if !fn(k, v[0]) {
				return
			}
		}
	}
}

func (this *HttpRec) PackRequest2JSON(json *wrapper.SuperValue) {
	lst := this.ReqR.URL.Query()
	for k, v := range lst {
		if len(v) > 0 {
			json.SetKeyValue(k, v[0])
		}
	}
	if this.Req != nil {
		json.CopyFrom(this.Req)
	}
}

func (this *HttpRec) QryAsStringEx(key, def string, removeit bool) string {
	var str string
	found := false
	if this.Req != nil {
		this.Req.CheckExistsStrFunc(key, func(strv string) {
			str = strv
			if removeit {
				this.Req.RemoveKey(key)
			}
			found = true
		})
	}
	if !found {
		str = this.URLQuery().Get(key)
		if len(str) == 0 {
			return def
		}
	}
	return str
}

func (this *HttpRec) QryAsString(key, def string) string {
	return this.QryAsStringEx(key, def, false)
}

func (this *HttpRec) StringByName(key, def string) string {
	return this.QryAsStringEx(key, def, false)
}

func (this *HttpRec) QryAsFloat64(key string, def float64) float64 {
	str := this.ReqR.URL.Query().Get(key)
	if len(str) == 0 {
		if this.Req != nil {
			str = this.Req.StringByPath(key, "")
		} else {
			return def
		}
	}
	return utils.StrToFloat64Def(str, def)
}

func (this *HttpRec) QryAsDateTime(key string, def time.Time) time.Time {
	str := this.ReqR.URL.Query().Get(key)
	if len(str) != 0 {
		if this.Req != nil {
			return utils.TryStrToTime(str, def.Location(), def)
		} else {
			return def
		}
	}
	return this.Req.AsDateTime(key, def)
}

func (this *HttpRec) QryAsBool(key string, def bool) bool {
	str := this.QryAsString(key, "")
	if len(str) == 0 {
		return def
	}

	return utils.TryStrToBool(str, def)
}

func (this *HttpRec) SetValues(kvPairs ...interface{}) *HttpRec {
	this.Resp.SetKeyValues(kvPairs...)
	return this
}

func (this *HttpRec) ResponseText(text string) {
	this.ResponseBuf([]byte(text))
}

func (this *HttpRec) ResponseSyncListN(lst *utils.SyncCycleList, searchval string, maxn int) {
	ResponseCycleList(this.RespW, lst, searchval, maxn)
}

func (this *HttpRec) ResponseSyncListNEx(lst *utils.SyncCycleList) {
	if this.QryAsInt("clean", 0) == 1 {
		lst.Clear()
	}
	configMaxN := this.QryAsInt("configmax", 0)
	if configMaxN > 0 {
		lst.ConfigMaxSize(int32(configMaxN))
	}
	ResponseCycleList(this.RespW, lst, this.QryAsString("searchval", ""), this.QryAsInt("n", 100))
}

func (this *HttpRec) ResponseSyncList(lst *utils.SyncCycleList, searchval string) {
	ResponseCycleList(this.RespW, lst, searchval, 0)
}

func (this *HttpRec) WriteText(text string) {
	if this.RespFlag == 1 {
		return
	}

	if this.OnBeforeResponseFunc != nil {
		if !this.OnBeforeResponseFunc(this) {
			return
		}
	}

	if this.BeforeRespLs != nil {
		this.BeforeRespLs.Dispatch(this, text)
	}

	if this.RespW != nil {
		this.RespW.Write([]byte(text))

		this.innerDoAfterResponseFunc(this.RespW.Header(), []byte(text))

	}

}

func (this *HttpRec) ResponseBuf(buf []byte) {
	if !atomic.CompareAndSwapInt32(&this.RespFlag, 0, 1) {
		/// 已经响应完成不再响应
		return
	}

	if this.OnBeforeResponseFunc != nil {
		if !this.OnBeforeResponseFunc(this) {
			return
		}
	}

	if this.BeforeRespLs != nil {
		this.BeforeRespLs.Dispatch(this, buf)
	}

	if this.RespW != nil {
		this.RespW.Write(buf)
		this.innerDoAfterResponseFunc(this.RespW.Header(), buf)
	}

}

func (this *HttpRec) ResponseJSONValues(kvPairs ...interface{}) {
	if !atomic.CompareAndSwapInt32(&this.RespFlag, 0, 1) {
		/// 已经响应完成不再响应
		return
	}
	if this.RespW != nil {
		if len(kvPairs) == 1 {
			if resp, ok := kvPairs[0].(*wrapper.SuperValue); ok {
				this.RespW.Header().Set("content-type", "application/json")
				this.RespW.Write(resp.AsJSONBytes(this.EncodeJSONEscape))
			} else if str, ok := kvPairs[0].(string); ok {
				this.RespW.Header().Set("content-type", "application/json")
				this.RespW.Write([]byte(str))
			} else if buf, ok := kvPairs[0].([]byte); ok {
				this.RespW.Header().Set("content-type", "application/json")
				this.RespW.Write(buf)
			}
			return
		}
		this.SetValues(kvPairs...)
		this.innerResponseAsJSON()
		if this.AfterResponse != nil {
			this.AfterResponse()
		}
	}
}

func (this *HttpRec) ResponseCodeAndMsg(code int, msg string) {
	if this.RespW != nil {
		this.ResponseJSONValues("code", code, "msg", utils.EscapeJsonStrSimple(msg))
	}
}

func (this *HttpRec) ClientAddr() string {
	return GetHttpClientAddr(this.ReqR)
}

func (this *HttpRec) CheckResponseAsJson() {
	if !atomic.CompareAndSwapInt32(&this.RespFlag, 0, 1) {
		/// 已经响应完成不再响应
		return
	}
	this.innerResponseAsJSON()
}

func (this *HttpRec) ResponseAJSON(json *wrapper.SuperValue) {
	if this.RespW != nil {
		this.RespW.Header().Set("content-type", "application/json")

		if this.OnBeforeResponseFunc != nil {
			if !this.OnBeforeResponseFunc(this) {
				return
			}
		}

		if this.BeforeRespLs != nil {
			this.BeforeRespLs.Dispatch(this, json)
		}

		bodyBuf := json.AsJSONBytes(this.EncodeJSONEscape)
		this.RespW.Write(bodyBuf)

		this.innerDoAfterResponseFunc(this.RespW.Header(), []byte(bodyBuf))
	}
}

func (this *HttpRec) innerResponseAsJSON() {
	if this.RespW != nil {
		this.RespW.Header().Set("content-type", "application/json")

		if this.OnBeforeResponseFunc != nil {
			if !this.OnBeforeResponseFunc(this) {
				return
			}
		}

		if this.BeforeRespLs != nil {
			this.BeforeRespLs.Dispatch(this, this.Resp)
		}

		bodyBuf := this.Resp.AsJSONBytes(this.EncodeJSONEscape)
		this.RespW.Write(bodyBuf)

		this.innerDoAfterResponseFunc(this.RespW.Header(), []byte(bodyBuf))
	}
}

func (this *HttpRec) ResponseAsJSON() {
	if !atomic.CompareAndSwapInt32(&this.RespFlag, 0, 1) {
		/// 已经响应完成不再响应
		return
	}
	this.innerResponseAsJSON()
}
